--- /dev/null
+# Makefile for consoled
+# based on xcs Makefile
+# Anthony Liguori 2005
+
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CONSOLED_INSTALL_DIR = /usr/sbin
+XC_CONSOLE_INSTALL_DIR = /usr/libexec/xen
+
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+
+CC = gcc
+CFLAGS = -Wall -Werror -g3
+
+CFLAGS += -I $(XEN_XCS)
+CFLAGS += -I $(XEN_LIBXC)
+CFLAGS += -I $(XEN_XENSTORE)
+
+SRCS :=
+SRCS += main.c utils.c io.c
+
+HDRS = $(wildcard *.h)
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+BIN = consoled
+
+all: $(BIN) xc_console
+
+clean:
+ $(RM) *.a *.so *.o *.rpm $(BIN) xc_console
+
+$(BIN): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_XENSTORE) \
+ -lxc -lxenstore
+
+xc_console: xc_console.o
+ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_XENSTORE) \
+ -lxc -lxenstore
+
+$(OBJS): $(HDRS)
+
+install: $(BIN)
+ $(INSTALL_DIR) -p $(DESTDIR)/$(CONSOLED_INSTALL_DIR)
+ $(INSTALL_PROG) $(BIN) $(DESTDIR)/$(CONSOLED_INSTALL_DIR)
+ $(INSTALL_DIR) -p $(DESTDIR)/$(XC_CONSOLE_INSTALL_DIR)
+ $(INSTALL_PROG) xc_console $(DESTDIR)/$(XC_CONSOLE_INSTALL_DIR)
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#define _GNU_SOURCE
+
+#include "utils.h"
+#include "io.h"
+
+#include "xc.h"
+#include "xs.h"
+#include "xen/io/domain_controller.h"
+#include "xcs_proto.h"
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <termios.h>
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+struct buffer
+{
+ char *data;
+ size_t size;
+ size_t capacity;
+ size_t max_capacity;
+};
+
+void buffer_append(struct buffer *buffer, const void *data, size_t size)
+{
+ if ((buffer->capacity - buffer->size) < size) {
+ buffer->capacity += (size + 1024);
+ buffer->data = realloc(buffer->data, buffer->capacity);
+ if (buffer->data == NULL) {
+ dolog(LOG_ERR, "Memory allocation failed");
+ exit(ENOMEM);
+ }
+ }
+
+ memcpy(buffer->data + buffer->size, data, size);
+ buffer->size += size;
+
+ if (buffer->max_capacity &&
+ buffer->size > buffer->max_capacity) {
+ memmove(buffer->data + (buffer->size - buffer->max_capacity),
+ buffer->data, buffer->max_capacity);
+ buffer->data = realloc(buffer->data, buffer->max_capacity);
+ buffer->capacity = buffer->max_capacity;
+ }
+}
+
+bool buffer_empty(struct buffer *buffer)
+{
+ return buffer->size == 0;
+}
+
+void buffer_advance(struct buffer *buffer, size_t size)
+{
+ size = MIN(size, buffer->size);
+ memmove(buffer->data, buffer + size, buffer->size - size);
+ buffer->size -= size;
+}
+
+struct domain
+{
+ int domid;
+ int tty_fd;
+ struct buffer buffer;
+ struct domain *next;
+};
+
+static struct domain *dom_head;
+
+bool domain_is_valid(int domid)
+{
+ bool ret;
+ xc_dominfo_t info;
+
+ ret = (xc_domain_getinfo(xc, domid, 1, &info) == 1 &&
+ info.domid == domid);
+
+ return ret;
+}
+
+int domain_create_tty(int domid)
+{
+ char path[1024];
+ int master;
+
+ if ((master = getpt()) == -1 ||
+ grantpt(master) == -1 || unlockpt(master) == -1) {
+ dolog(LOG_ERR, "Failed to create tty for domain-%d", domid);
+ master = -1;
+ } else {
+ const char *slave = ptsname(master);
+ struct termios term;
+
+ if (tcgetattr(master, &term) != -1) {
+ cfmakeraw(&term);
+ tcsetattr(master, TCSAFLUSH, &term);
+ }
+
+ xs_mkdir(xs, "/console");
+ snprintf(path, sizeof(path), "/console/%d", domid);
+ xs_mkdir(xs, path);
+ strcat(path, "/tty");
+
+ xs_write(xs, path, slave, strlen(slave), O_CREAT);
+ }
+
+ return master;
+}
+
+struct domain *create_domain(int domid)
+{
+ struct domain *dom;
+ char *data;
+ unsigned int len;
+ char path[1024];
+
+ dom = (struct domain *)malloc(sizeof(struct domain));
+ if (dom == NULL) {
+ dolog(LOG_ERR, "Out of memory %s:%s():L%d",
+ __FILE__, __FUNCTION__, __LINE__);
+ exit(ENOMEM);
+ }
+
+ dom->domid = domid;
+ dom->tty_fd = domain_create_tty(domid);
+ dom->buffer.data = 0;
+ dom->buffer.size = 0;
+ dom->buffer.capacity = 0;
+ dom->buffer.max_capacity = 0;
+
+ snprintf(path, sizeof(path), "/console/%d/limit", domid);
+ data = xs_read(xs, path, &len);
+ if (data) {
+ dom->buffer.max_capacity = strtoul(data, 0, 0);
+ free(data);
+ }
+
+ dolog(LOG_DEBUG, "New domain %d", domid);
+
+ return dom;
+}
+
+struct domain *lookup_domain(int domid)
+{
+ struct domain **pp;
+
+ for (pp = &dom_head; *pp; pp = &(*pp)->next) {
+ struct domain *dom = *pp;
+
+ if (dom->domid == domid) {
+ return dom;
+ } else if (dom->domid > domid) {
+ *pp = create_domain(domid);
+ (*pp)->next = dom;
+ return *pp;
+ }
+ }
+
+ *pp = create_domain(domid);
+ return *pp;
+}
+
+void remove_domain(struct domain *dom)
+{
+ struct domain **pp;
+
+ dolog(LOG_DEBUG, "Removing domain-%d", dom->domid);
+
+ for (pp = &dom_head; *pp; pp = &(*pp)->next) {
+ struct domain *d = *pp;
+
+ if (dom->domid == d->domid) {
+ *pp = d->next;
+ free(d);
+ break;
+ }
+ }
+}
+
+void handle_tty_read(struct domain *dom)
+{
+ ssize_t len;
+ xcs_msg_t msg;
+
+ msg.type = XCS_REQUEST;
+ msg.u.control.remote_dom = dom->domid;
+ msg.u.control.msg.type = CMSG_CONSOLE;
+ msg.u.control.msg.subtype = CMSG_CONSOLE_DATA;
+ msg.u.control.msg.id = 1;
+
+ len = read(dom->tty_fd, msg.u.control.msg.msg, 60);
+ if (len < 1) {
+ close(dom->tty_fd);
+
+ if (domain_is_valid(dom->domid)) {
+ dom->tty_fd = domain_create_tty(dom->domid);
+ } else {
+ remove_domain(dom);
+ }
+ } else if (domain_is_valid(dom->domid)) {
+ msg.u.control.msg.length = len;
+
+ if (!write_sync(xcs_data_fd, &msg, sizeof(msg))) {
+ dolog(LOG_ERR, "Write to xcs failed: %m");
+ }
+ } else {
+ close(dom->tty_fd);
+ remove_domain(dom);
+ }
+}
+
+void handle_tty_write(struct domain *dom)
+{
+ ssize_t len;
+
+ len = write(dom->tty_fd, dom->buffer.data, dom->buffer.size);
+ if (len < 1) {
+ close(dom->tty_fd);
+
+ if (domain_is_valid(dom->domid)) {
+ dom->tty_fd = domain_create_tty(dom->domid);
+ } else {
+ remove_domain(dom);
+ }
+ } else {
+ buffer_advance(&dom->buffer, len);
+ }
+}
+
+void handle_xcs_msg(int fd)
+{
+ xcs_msg_t msg;
+
+ if (!read_sync(fd, &msg, sizeof(msg))) {
+ dolog(LOG_ERR, "read from xcs failed! %m");
+ } else if (msg.type == XCS_REQUEST) {
+ struct domain *dom;
+
+ dom = lookup_domain(msg.u.control.remote_dom);
+ buffer_append(&dom->buffer,
+ msg.u.control.msg.msg,
+ msg.u.control.msg.length);
+ }
+}
+
+static void enum_domains(void)
+{
+ int domid = 0;
+ xc_dominfo_t dominfo;
+
+ while (xc_domain_getinfo(xc, domid, 1, &dominfo) == 1) {
+ lookup_domain(dominfo.domid);
+ domid = dominfo.domid + 1;
+ }
+}
+
+void handle_io(void)
+{
+ fd_set readfds, writefds;
+ int ret;
+ int max_fd = -1;
+
+ do {
+ struct domain *d;
+ struct timeval tv = { 1, 0 };
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+
+ FD_SET(xcs_data_fd, &readfds);
+ max_fd = MAX(xcs_data_fd, max_fd);
+
+ for (d = dom_head; d; d = d->next) {
+ if (d->tty_fd != -1) {
+ FD_SET(d->tty_fd, &readfds);
+ }
+
+ if (d->tty_fd != -1 && !buffer_empty(&d->buffer)) {
+ FD_SET(d->tty_fd, &writefds);
+ }
+
+ max_fd = MAX(d->tty_fd, max_fd);
+ }
+
+ ret = select(max_fd + 1, &readfds, &writefds, 0, &tv);
+ enum_domains();
+
+ if (FD_ISSET(xcs_data_fd, &readfds)) {
+ handle_xcs_msg(xcs_data_fd);
+ }
+
+ for (d = dom_head; d; d = d->next) {
+ if (FD_ISSET(d->tty_fd, &readfds)) {
+ handle_tty_read(d);
+ }
+
+ if (FD_ISSET(d->tty_fd, &writefds)) {
+ handle_tty_write(d);
+ }
+ }
+ } while (ret > -1);
+}
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#ifndef CONSOLED_IO_H
+#define CONSOLED_IO_H
+
+void handle_io(void);
+
+#endif
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "xc.h"
+#include "xen/io/domain_controller.h"
+#include "xcs_proto.h"
+
+#include "utils.h"
+#include "io.h"
+
+int main(int argc, char **argv)
+{
+ const char *sopts = "hVvi";
+ struct option lopts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'V' },
+ { "verbose", 0, 0, 'v' },
+ { "interactive", 0, 0, 'i' },
+ { 0 },
+ };
+ bool is_interactive = false;
+ int ch;
+ int syslog_option = LOG_CONS;
+ int syslog_mask = LOG_WARNING;
+ int opt_ind = 0;
+
+ while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) {
+ switch (ch) {
+ case 'h':
+ //usage(argv[0]);
+ exit(0);
+ case 'V':
+ //version(argv[0]);
+ exit(0);
+ case 'v':
+ syslog_option |= LOG_PERROR;
+ syslog_mask = LOG_DEBUG;
+ break;
+ case 'i':
+ is_interactive = true;
+ break;
+ case '?':
+ fprintf(stderr,
+ "Try `%s --help' for more information\n",
+ argv[0]);
+ exit(EINVAL);
+ }
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "%s requires root to run.\n", argv[0]);
+ exit(EPERM);
+ }
+
+ openlog("consoled", syslog_option, LOG_DAEMON);
+ setlogmask(syslog_mask);
+
+ if (!is_interactive) {
+ daemonize("/var/run/consoled.pid");
+ }
+
+ xen_setup();
+
+ handle_io();
+
+ closelog();
+
+ return 0;
+}
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <string.h>
+
+#include "xc.h"
+#include "xen/io/domain_controller.h"
+#include "xcs_proto.h"
+
+#include "utils.h"
+
+struct xs_handle *xs;
+int xc;
+
+int xcs_ctrl_fd = -1;
+int xcs_data_fd = -1;
+
+bool _read_write_sync(int fd, void *data, size_t size, bool do_read)
+{
+ size_t offset = 0;
+ ssize_t len;
+
+ while (offset < size) {
+ if (do_read) {
+ len = read(fd, data + offset, size - offset);
+ } else {
+ len = write(fd, data + offset, size - offset);
+ }
+
+ if (len < 1) {
+ if (len == -1 && (errno == EAGAIN || errno == EINTR)) {
+ return false;
+ }
+ } else {
+ offset += len;
+ }
+ }
+
+ return true;
+}
+
+static int open_domain_socket(const char *path)
+{
+ struct sockaddr_un addr;
+ int sock;
+ size_t addr_len;
+
+ if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+ goto out;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+ addr_len = sizeof(addr.sun_family) + strlen(XCS_SUN_PATH) + 1;
+
+ if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) {
+ goto out_close_sock;
+ }
+
+ return sock;
+
+ out_close_sock:
+ close(sock);
+ out:
+ return -1;
+}
+
+static void child_exit(int sig)
+{
+ while (waitpid(-1, NULL, WNOHANG) > 0);
+}
+
+void daemonize(const char *pidfile)
+{
+ pid_t pid;
+ int fd;
+ int len;
+ int i;
+ char buf[100];
+
+ if (getppid() == 1) {
+ return;
+ }
+
+ if ((pid = fork()) > 0) {
+ exit(0);
+ } else if (pid == -1) {
+ err(errno, "fork() failed");
+ }
+
+ setsid();
+
+ /* redirect fd 0,1,2 to /dev/null */
+ if ((fd = open("/dev/null",O_RDWR)) == -1) {
+ exit(1);
+ }
+
+ for (i = 0; i <= 2; i++) {
+ close(i);
+ dup2(fd, i);
+ }
+
+ close(fd);
+
+ umask(027);
+ chdir("/");
+
+ fd = open(pidfile, O_RDWR | O_CREAT);
+ if (fd == -1) {
+ exit(1);
+ }
+
+ if (lockf(fd, F_TLOCK, 0) == -1) {
+ exit(1);
+ }
+
+ len = sprintf(buf, "%d\n", getpid());
+ write(fd, buf, len);
+
+ signal(SIGCHLD, child_exit);
+ signal(SIGTSTP, SIG_IGN);
+ signal(SIGTTOU, SIG_IGN);
+ signal(SIGTTIN, SIG_IGN);
+}
+
+/* synchronized send/recv strictly for setting up xcs */
+/* always use asychronize callbacks any other time */
+static bool xcs_send_recv(int fd, xcs_msg_t *msg)
+{
+ bool ret = false;
+
+ if (!write_sync(fd, msg, sizeof(*msg))) {
+ dolog(LOG_ERR, "Write failed at %s:%s():L%d? Possible bug.",
+ __FILE__, __FUNCTION__, __LINE__);
+ goto out;
+ }
+
+ if (!read_sync(fd, msg, sizeof(*msg))) {
+ dolog(LOG_ERR, "Read failed at %s:%s():L%d? Possible bug.",
+ __FILE__, __FUNCTION__, __LINE__);
+ goto out;
+ }
+
+ ret = true;
+
+ out:
+ return ret;
+}
+
+bool xen_setup(void)
+{
+ int sock;
+ xcs_msg_t msg;
+
+ xs = xs_daemon_open();
+ if (xs == NULL) {
+ dolog(LOG_ERR,
+ "Failed to contact xenstore (%m). Is it running?");
+ goto out;
+ }
+
+ xc = xc_interface_open();
+ if (xc == -1) {
+ dolog(LOG_ERR, "Failed to contact hypervisor (%m)");
+ goto out;
+ }
+
+ sock = open_domain_socket(XCS_SUN_PATH);
+ if (sock == -1) {
+ dolog(LOG_ERR, "Failed to contact xcs (%m). Is it running?");
+ goto out_close_store;
+ }
+
+ xcs_ctrl_fd = sock;
+
+ sock = open_domain_socket(XCS_SUN_PATH);
+ if (sock == -1) {
+ dolog(LOG_ERR, "Failed to contact xcs (%m). Is it running?");
+ goto out_close_ctrl;
+ }
+
+ xcs_data_fd = sock;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = XCS_CONNECT_CTRL;
+ if (!xcs_send_recv(xcs_ctrl_fd, &msg) || msg.result != XCS_RSLT_OK) {
+ dolog(LOG_ERR, "xcs control connect failed. Possible bug.");
+ goto out_close_data;
+ }
+
+ msg.type = XCS_CONNECT_DATA;
+ if (!xcs_send_recv(xcs_data_fd, &msg) || msg.result != XCS_RSLT_OK) {
+ dolog(LOG_ERR, "xcs data connect failed. Possible bug.");
+ goto out_close_data;
+ }
+
+ /* Since the vast majority of control messages are console messages
+ it's just easier to ignore other messages that try to bind to
+ a specific type. */
+ msg.type = XCS_MSG_BIND;
+ msg.u.bind.port = PORT_WILDCARD;
+ msg.u.bind.type = TYPE_WILDCARD;
+ if (!xcs_send_recv(xcs_ctrl_fd, &msg) || msg.result != XCS_RSLT_OK) {
+ dolog(LOG_ERR, "xcs vind failed. Possible bug.");
+ goto out_close_data;
+ }
+
+ return true;
+
+ out_close_data:
+ close(xcs_ctrl_fd);
+ xcs_data_fd = -1;
+ out_close_ctrl:
+ close(xcs_ctrl_fd);
+ xcs_ctrl_fd = -1;
+ out_close_store:
+ xs_daemon_close(xs);
+ out:
+ return false;
+}
+
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#ifndef CONSOLED_UTILS_H
+#define CONSOLED_UTILS_H
+
+#include <stdbool.h>
+#include <syslog.h>
+#include <stdio.h>
+
+#include "xs.h"
+
+void daemonize(const char *pidfile);
+bool xen_setup(void);
+#define read_sync(fd, buffer, size) _read_write_sync(fd, buffer, size, true)
+#define write_sync(fd, buffer, size) _read_write_sync(fd, buffer, size, false)
+bool _read_write_sync(int fd, void *data, size_t size, bool do_read);
+
+extern int xcs_ctrl_fd;
+extern int xcs_data_fd;
+extern struct xs_handle *xs;
+extern int xc;
+
+#if 1
+#define dolog(val, fmt, ...) syslog(val, fmt, ## __VA_ARGS__)
+#else
+#define dolog(val, fmt, ...) fprintf(stderr, fmt "\n", ## __VA_ARGS__)
+#endif
+
+#endif
--- /dev/null
+/*\
+ * Copyright (C) International Business Machines Corp., 2005
+ * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * Xen Console Daemon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+\*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/select.h>
+#include <err.h>
+#include <errno.h>
+#include <pty.h>
+
+#include "xc.h"
+#include "xs.h"
+
+#define ESCAPE_CHARACTER 0x1d
+
+static volatile sig_atomic_t received_signal = 0;
+
+static void sighandler(int signum)
+{
+ received_signal = 1;
+}
+
+static bool write_sync(int fd, const void *data, size_t size)
+{
+ size_t offset = 0;
+ ssize_t len;
+
+ while (offset < size) {
+ len = write(fd, data + offset, size - offset);
+ if (len < 1) {
+ return false;
+ }
+ offset += len;
+ }
+
+ return true;
+}
+
+static void usage(const char *program) {
+ printf("Usage: %s [OPTION] DOMID\n"
+ "Attaches to a virtual domain console\n"
+ "\n"
+ " -h, --help display this help and exit\n"
+ , program);
+}
+
+/* don't worry too much if setting terminal attributes fail */
+static void init_term(int fd, struct termios *old)
+{
+ struct termios new_term;
+
+ if (tcgetattr(fd, old) == -1) {
+ perror("tcgetattr() failed");
+ return;
+ }
+
+ new_term = *old;
+ cfmakeraw(&new_term);
+
+ if (tcsetattr(fd, TCSAFLUSH, &new_term) == -1) {
+ perror("tcsetattr() failed");
+ }
+}
+
+static void restore_term(int fd, struct termios *old)
+{
+ if (tcsetattr(fd, TCSAFLUSH, old) == -1) {
+ perror("tcsetattr() failed");
+ }
+}
+
+static int console_loop(int xc_handle, domid_t domid, int fd)
+{
+ int ret;
+
+ do {
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ FD_SET(STDIN_FILENO, &fds);
+ FD_SET(fd, &fds);
+
+ ret = select(fd + 1, &fds, NULL, NULL, NULL);
+ if (ret == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ perror("select() failed");
+ return -1;
+ }
+
+ if (FD_ISSET(STDIN_FILENO, &fds)) {
+ ssize_t len;
+ char msg[60];
+
+ len = read(STDIN_FILENO, msg, sizeof(msg));
+ if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
+ return 0;
+ }
+
+ if (len == 0 && len == -1) {
+ if (len == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+ perror("select() failed");
+ return -1;
+ }
+
+ if (!write_sync(fd, msg, len)) {
+ perror("write() failed");
+ return -1;
+ }
+ }
+
+ if (FD_ISSET(fd, &fds)) {
+ ssize_t len;
+ char msg[512];
+
+ len = read(fd, msg, sizeof(msg));
+ if (len == 0 || len == -1) {
+ if (len == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+ perror("select() failed");
+ return -1;
+ }
+
+ if (!write_sync(STDOUT_FILENO, msg, len)) {
+ perror("write() failed");
+ return -1;
+ }
+ }
+ } while (received_signal == 0);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct termios attr;
+ int domid;
+ int xc_handle;
+ char *sopt = "hf:pc";
+ int ch;
+ int opt_ind=0;
+ struct option lopt[] = {
+ { "help", 0, 0, 'h' },
+ { "file", 1, 0, 'f' },
+ { "pty", 0, 0, 'p' },
+ { "ctty", 0, 0, 'c' },
+ { 0 },
+
+ };
+ char *str_pty;
+ char path[1024];
+ int spty;
+ unsigned int len = 0;
+ struct xs_handle *xs;
+
+ while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
+ switch(ch) {
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1) {
+ fprintf(stderr, "Invalid number of arguments\n");
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(EINVAL);
+ }
+
+ domid = atoi(argv[optind]);
+
+ xs = xs_daemon_open();
+ if (xs == NULL) {
+ err(errno, "Could not contact XenStore");
+ }
+
+ xc_handle = xc_interface_open();
+ if (xc_handle == -1) {
+ err(errno, "xc_interface_open()");
+ }
+
+ signal(SIGTERM, sighandler);
+
+ snprintf(path, sizeof(path), "/console/%d/tty", domid);
+ str_pty = xs_read(xs, path, &len);
+ if (str_pty == NULL) {
+ err(errno, "Could not read tty from store");
+ }
+ spty = open(str_pty, O_RDWR | O_NOCTTY);
+ if (spty == -1) {
+ err(errno, "Could not open tty `%s'", str_pty);
+ }
+ free(str_pty);
+
+ init_term(STDIN_FILENO, &attr);
+ console_loop(xc_handle, domid, spty);
+ restore_term(STDIN_FILENO, &attr);
+
+ return 0;
+ }